package gggameserver.testbed;

import gggameserver.compiler.GGCompiler;
import gggameserver.compiler.GGSolutionData;
import gggameserver.exceptions.GGProcessException;
import gggameserver.games.connection.ProcessConnection;
import gggameserver.util.GGConfiguration;
import gggameserver.util.GGUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.zip.ZipFile;

import org.apache.log4j.Logger;

public class GGLocalTestBed implements GGTestBed {

    private static org.apache.log4j.Logger log = Logger.getLogger(GGLocalTestBed.class);

	InputStream data = null;
	File dir = null;
	String filename = null;
	File data_file = null; // file where data is writen (deleted after unpack)
	
	File data_dir = null; // where the actual work starts
	String unpack_output = null;
	
	GGCompiler compiler = null;
	
	boolean valid = true;
	Exception exception = null;
	
	ArrayList processes = new ArrayList();

	boolean unbackNeeded = true;
	boolean deleteAfterUse = true;
	
	public GGLocalTestBed() {
		;
	}
	
	public GGLocalTestBed(InputStream data, File dir) throws IOException {
		this.initialize(data, dir);
	}
	
	public GGLocalTestBed(File dir) {
		//this.initialize(data, dir);
		this.dir = dir;
		this.unbackNeeded = false;
		this.deleteAfterUse = false;
	}
	
	
	/**
	 * 
	 * @param data
	 * @param dir test-bed directory, must exist
	 * @throws IOException 
	 */
	public void initialize(InputStream data, File dir) throws IOException {
		this.data = data;
		this.dir = dir;
		this.filename = "solution.zip";
		createFile();
	}

	private void createFile() throws IOException {
		data_file = new File(dir, filename);
		FileOutputStream file = new FileOutputStream(data_file);
		byte[] buffer = new byte[4096];
		int len = 0;
		while ((len = data.read(buffer)) >= 0)
			file.write(buffer, 0, len);
		data.close();
		file.close();
	}
	
	public void unpack() throws IOException, GGProcessException {
		if (!unbackNeeded) return; // TODO any better solution?
		
		// TODO 1) security - can't pack anywhere
		ArrayList cmdarray = GGUtil.getUnzipCommandline(filename);
		Process p = GGUtil.exec(cmdarray, dir);
		unpack_output = GGUtil.collectOutput(p);
		if (p.exitValue() != 0) 
			throw new GGProcessException("Error while unpacking solution", 
					cmdarray, p.exitValue(), unpack_output);
		if (!data_file.delete())
			throw new IOException("Couldn't delete data-file " + data_file.getAbsolutePath());
	}
	
	public GGCompiler getCompiler(String language) throws IOException, GGProcessException {
		data_dir = dir;
		
		// if zip-file had root directory
		String relative_dir = ".";
		File[] dir_content = data_dir.listFiles();
		if (dir_content.length == 1 && dir_content[0].isDirectory()) {
			data_dir = dir_content[0];
			relative_dir = dir_content[0].getName();
		}
		
		log.debug("Compiling in " + data_dir.getAbsolutePath());
		
		compiler = new GGCompiler(language, data_dir, relative_dir);
		
		return compiler;
	}
	
	public void compile(String language) throws IOException, GGProcessException {
		compiler = getCompiler(language);
		compiler.compile();
		//return compiler;
	}
	
	public void setLanguage(String language) throws IOException, GGProcessException {
		getCompiler(language);
	}
	
	public void setInvalid(Exception e) {
		this.valid = false;
		this.exception = e;
	}
	
	public boolean isValid() {
		return this.valid;
	}

	public String getLog() {
		if (null == this.exception) return null;
		return this.exception.toString();
	}

	public ProcessConnection createConnection() throws IOException {
		if (!isValid())
			throw new IllegalStateException("Can't create connection for invalid test-bed (exception" + this.exception + ")");
		if (null == compiler)
			throw new IllegalStateException("Solution not compiled");

		closeProcesses();
		ProcessConnection pc = new ProcessConnection(
				compiler.getProgramsCommand(),
				compiler.getProgramsDirectory());
		processes.add(pc);
		
		return pc;
	}
	
	public void close() {
		// TODO Auto-generated method stub for test-bed close
		try {
			closeProcesses();
			if (this.deleteAfterUse && GGConfiguration.getProperty("testbed.delete_old").equalsIgnoreCase("yes"))
				GGUtil.deleteDirectory(this.dir);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	private void closeProcesses() {
		for (int i = 0; i < processes.size(); i++) {
			ProcessConnection pc = (ProcessConnection)processes.get(i);
			pc.close();
		}
		processes.clear();
	}

	public File zipTestBed() throws GGProcessException {
		File zipFile = null;
		try {
			System.out.println("zip-ing local testbed...");
			zipFile = new File(this.dir.getParentFile(), this.dir.getName() + ".zip");
			
			ArrayList<String> cmdarray = new ArrayList<String>();
			cmdarray.add("7z");
			cmdarray.add("u"); // update
			cmdarray.add("-tzip");
			cmdarray.add("-r");
			cmdarray.add(zipFile.getCanonicalPath());
			cmdarray.add(this.dir.getCanonicalPath());
	
			Process zipper = GGUtil.exec(cmdarray, new File("."));
			
			String output = GGUtil.collectOutput(zipper);
	System.out.println("out:\"" + output + "\"");
			
			if (zipper.exitValue() != 0)
				throw new GGProcessException("Error while zip'ing testbed.", cmdarray, zipper.exitValue(), output);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			// TODO
		}
		
		return zipFile;
	}
	
}
